/// 
/// MaDAson - Toolkit for Model Driven Software Development
/// 
/// Copyright [2006] Marcus Lankenau
/// 
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///  
///     http://www.apache.org/licenses/LICENSE-2.0
///  
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
/// 

using System;
using System.IO;
using System.Collections.Generic;
using System.Text;

namespace Madason.TemplateCompiler
{
	public class ParserException : Exception
	{
		public ParserException(string message) : base(message)
		{			
		}
	}

	public class Parser
	{
		#region definitions

		enum ParserState
		{
			Default,
			Scriptlet,
			CodeBlock,
			EOF,
			Comment
		}
		
		#endregion

		const string TOKEN_COMMENT_BEGIN = "<!--";
		const string TOKEN_CODEBLOCK_BEGIN = "<script runat=\"server\">";
		const string TOKEN_SCRIPTLET_BEGIN = "<%";
		const string TOKEN_COMMENT_END = "-->";
		const string TOKEN_CODEBLOCK_END = "</script>";
		const string TOKEN_SCRIPTLET_END = "%>";

		//private TextReader reader;
		private ParserState state = ParserState.Default;
		string inputString;
		int currentPos = 0;
		int currentLine = 1;

		public Parser(TextReader reader)
		{
			inputString = reader.ReadToEnd();
			//this.reader = reader;
		}

		public char NextChar()
		{
			if (currentPos >= inputString.Length) return (char)0;

			char c = inputString[currentPos];
			currentPos++;
			return c;
		}

		public bool EOF()
		{
			return  (currentPos >= inputString.Length);

		}

		public bool IsNext(string token)
		{
			if (inputString.Length - currentPos < token.Length) return false;
			return inputString.Substring(currentPos, token.Length) == token;
		}

		
		public void IgnoreNext(int count)
		{
			currentPos+=count;
		}

		public void Append(StringBuilder sb, char c)
		{
			if (c != 0) sb.Append(c);
		}


		public Token ReadToken()
		{
			int lineBeginning = currentLine;
			if (state == ParserState.EOF)
				return new Token(TokenType.EOF, "");
			
			StringBuilder buffer = new StringBuilder();
			while (true)
			{


				if (state == ParserState.Default)
				{
					if (EOF())
					{
						state = ParserState.EOF;
						return new Token(TokenType.Text, buffer.ToString());
					}

					if (IsNext(TOKEN_SCRIPTLET_BEGIN))
					{
						state = ParserState.Scriptlet;
						IgnoreNext(TOKEN_SCRIPTLET_BEGIN.Length);
						return new Token(TokenType.Text, buffer.ToString());
					}

					if (IsNext(TOKEN_CODEBLOCK_BEGIN))
					{
						state = ParserState.CodeBlock;
						IgnoreNext(TOKEN_CODEBLOCK_BEGIN.Length);
						return new Token(TokenType.Text, buffer.ToString());
					}

					if (IsNext(TOKEN_COMMENT_BEGIN))
					{
						state = ParserState.Comment;
						IgnoreNext(TOKEN_COMMENT_BEGIN.Length);
						return new Token(TokenType.Text, buffer.ToString());
					}
				}
				else if (state == ParserState.Scriptlet)
				{
					if (EOF())
					{
						throw new ParserException("scriptlet is not closed");
					}

					if (IsNext(TOKEN_SCRIPTLET_END))
					{
						state = ParserState.Default;
						IgnoreNext(TOKEN_SCRIPTLET_END.Length);
						Token t = new Token(TokenType.Scriptlet, buffer.ToString());
						t.Line = lineBeginning;
						return t;
					}
				}
				else if (state == ParserState.CodeBlock)
				{
					if (EOF())
					{
						throw new ParserException("codeblock is not closed");
					}

					if (IsNext(TOKEN_CODEBLOCK_END))
					{
						state = ParserState.Default;
						IgnoreNext(TOKEN_CODEBLOCK_END.Length);
						Token t = new Token(TokenType.CodeBlock, buffer.ToString());
						t.Line = lineBeginning;
						return t;
					}
				}
				else if (state == ParserState.Comment)
				{
					if (IsNext(TOKEN_COMMENT_END))
					{
						state = ParserState.Default;
						IgnoreNext(TOKEN_COMMENT_END.Length);
						buffer = new StringBuilder();
					}
				}

				char c = NextChar();
				if (c == '\n') currentLine++;
				Append(buffer, c);
			}
		}


	}


}
